interface Handle {
    (next: (data?: any) => boolean, retry: (timeout?: number) => boolean | 'ASYNC', end: (data?: any) => boolean): void
}

interface Finish {
    (success?: boolean, results?: any[]): void
}

class TaskItem {
    private handle: Handle = null;

    constructor(handle: Handle) {
        this.handle = handle;
    }

    public excute(next: (data?: any) => boolean, retry: (timeout?: number) => boolean | 'ASYNC', end: (data?: any) => boolean) {
        this.handle(next, retry, end);
    }
}

/**
 * 顺序执行
 */
class Sync {
    private running = false;
    private index: number = -1;
    private list: TaskItem[] = [];
    private finish: Finish = null;

    // 每个item的返回值，通过next或end存储
    public results: any[] = [];

    public size(): number {
        return this.list.length;
    }

    public add(handle: Handle): Sync {
        this.list.push(new TaskItem(handle));
        this.results.push(undefined);
        return this;
    }

    public start(finish?: Finish): Sync {
        if (this.running) {
            return this;
        }

        this.running = true;
        this.index = -1;
        this.finish = finish;
        this.next();

        return this;
    }

    public stop(): boolean {
        if (!this.running) {
            return false;
        }

        this.running = false;
        this.finish && this.finish(false, this.results);

        return true;
    }

    public isStop() {
        return !this.running;
    }

    private end(data?: any): boolean {
        if (!this.running) {
            return false;
        }

        if (typeof data !== 'undefined') {
            this.results[this.index] = data;
        }

        this.running = false;
        this.finish && this.finish(true, this.results);

        return true;
    }

    private next(data?: any): boolean {
        if (!this.running) {
            return false;
        }

        if (typeof data !== 'undefined') {
            this.results[this.index] = data;
        }

        if (++this.index < this.list.length) {
            this.retry();
        } else {
            this.end();
        }

        return true;
    }

    private retry(): boolean {
        if (!this.running) {
            return false;
        }

        const taskItem = this.list[this.index];
        taskItem && taskItem.excute(
            (data?: any) => this.next(data),
            (timeout = 0) => timeout > 0 ? (setTimeout(() => this.retry(), timeout * 1000), 'ASYNC') : this.retry(),
            (data?: any) => this.end(data)
        );

        return true;
    }
}

class ASync {
    private running = false;
    private count: number = 0;
    private list: TaskItem[] = [];
    private finish: Finish = null;

    // 每个item的返回值，通过next或end存储
    public results: any[] = [];

    public size(): number {
        return this.list.length;
    }

    public add(handle: Handle): ASync {
        this.list.push(new TaskItem(handle));
        this.results.push(undefined);

        if (this.running) {
            this.retry(this.list.length - 1);
        }
        return this;
    }

    public start(finish?: Finish): ASync {
        if (this.running) {
            return this;
        }

        this.running = true;
        this.count = 0;
        this.finish = finish;

        if (this.list.length) {
            for (let index = 0; index < this.list.length; index++) {
                this.retry(index);
            }
        } else {
            this.end && this.end();
        }

        return this;
    }

    public stop(): boolean {
        if (!this.running) {
            return false;
        }
        this.running = false;
        this.finish && this.finish(false, this.results)

        return true;
    }

    public isStop() {
        return !this.running;
    }

    private end(index?: number, data?: any): boolean {
        if (!this.running) {
            return false;
        }

        if (typeof data !== 'undefined' && typeof index !== 'undefined') {
            this.results[index] = data;
        }

        this.running = false;
        this.finish && this.finish(true, this.results);

        return true;
    }

    private next(index?: number, data?: any): boolean {
        if (!this.running) {
            return false;
        }

        if (typeof data !== 'undefined' && typeof index !== 'undefined') {
            this.results[index] = data;
        }

        if (++this.count === this.list.length) {
            this.end && this.end();
        }

        return true;
    }

    private retry(index: number): boolean {
        if (!this.running) {
            return false;
        }

        const taskItem = this.list[index];
        taskItem && taskItem.excute(
            (data?: any) => this.next(index, data),
            (timeout = 0) => timeout > 0 ? (setTimeout(() => this.retry(index), timeout * 1000), 'ASYNC') : this.retry(index),
            (data?: any) => this.end(index, data)
        );

        return true;
    }
}

const task = {
    /**
     * 任务顺序执行
     */
    createSync(): Sync {
        return new Sync();
    },

    /**
     * 任务同时执行
     */
    createASync(): ASync {
        return new ASync();
    },

    excute(fun: (retry: (timeout?: number) => void) => void) {
        fun(function retry(timeout = 0) {
            if (timeout > 0) {
                setTimeout(() => task.excute(fun), timeout * 1000);
            } else {
                task.excute(fun);
            }
        })
    }
}

export default task;